package com.example.demo.widget.chart.histogram.horizontal

import android.annotation.SuppressLint
import android.content.Context
import android.graphics.*
import android.os.Build
import android.support.annotation.ColorInt
import android.support.annotation.RequiresApi
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
import com.example.demo.R
import com.example.demo.widget.chart.histogram.DrawInfoBuilder
import com.example.demo.widget.chart.histogram.DrawInfoHelper
import com.example.demo.widget.chart.histogram.HistogramData
import com.example.demo.widget.chart.histogram.HistogramItem
import com.example.demo.widget.chart.utils.ChartTextUtils
import com.example.demo.widget.chart.utils.DrawTextUtils
import kotlin.math.ceil
import kotlin.math.cos
import kotlin.math.roundToInt
import kotlin.math.sin

/**
 * 横向柱状图
 *
 * @author wangheng
 */
class HorizontalHistogramView: View {
    constructor(context: Context?) : super(context){
        initView(context,null)
    }
    constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs){
        initView(context,attrs)
    }
    constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
        context,
        attrs,
        defStyleAttr
    ){
        initView(context,attrs)
    }

    @RequiresApi(Build.VERSION_CODES.LOLLIPOP)
    constructor(
        context: Context?,
        attrs: AttributeSet?,
        defStyleAttr: Int,
        defStyleRes: Int
    ) : super(context, attrs, defStyleAttr, defStyleRes){
        initView(context,attrs)
    }

    private var mData: HistogramData? = null

    // x轴刻度文案颜色
    private var mXAxisScaleTextColor = Color.BLACK
    // x轴刻度文案文字大小
    private var mXAxisScaleTextSize = 12f
    // x轴刻度文案和刻度的距离
    private var mXAxisScaleTextPadding = 10f

    // x轴文字倾斜角度
    private var mXAxisScaleTextAngle = 0f

    // y轴刻度文案颜色
    private var mYAxisScaleTextColor = Color.BLACK
    // y轴刻度文案文字大小
    private var mYAxisScaleTextSize = 12f
    // y轴刻度文案和刻度的距离
    private var mYAxisScaleTextPadding = 10f

    // y轴文字最大长度
    private var mYAxisScaleTextMaxCount = 4

    // 总数文字大小
    private var mTotalTextSize = 12f
    // 总数文字颜色
    private var mTotalTextColor = Color.BLACK
    // 总数到图形的距离
    private var mTotalTextPadding = 10f
    // 总数是否加粗
    private var mTotalTextBold = false

    // 每个线条的高度
    private var mStripHeight = 10f
    // 线条的间隔
    private var mStripPadding = 10f
    // 线条的分割线宽度
    private var mStripPartDivider = 1f

    private var mChartToLeft = 0f
    private var mChartToRight = 0f
    private var mChartToTop = 0f
    private var mChartToBottom = 0f

    // 刻度虚线线宽
    private var mDashLineWidth = 10f
    // 刻度虚线空白宽度
    private var mDashLineSpace = 5f
    // 刻度虚线高度
    private var mDashLineHeight = 3f
    // 刻度虚线颜色
    private var mDashLineColor = Color.BLACK

    // X轴刻度文字列表
    private var mXAxisScaleTextList = ArrayList<String>()
    // paint
    private val mXAxisScaleTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)

    private val mYAxisScaleTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)

    private val mStripPaint = Paint(Paint.ANTI_ALIAS_FLAG)
    private val mTotalTextPaint = Paint(Paint.ANTI_ALIAS_FLAG)

    private val mDashLinePaint = Paint(Paint.ANTI_ALIAS_FLAG)

    private var mDrawInfoHelper: DrawInfoHelper? = null

    @SuppressLint("CustomViewStyleable")
    private fun initView(context: Context?, attrs: AttributeSet?) {
        if(context != null){
            val ta = context.obtainStyledAttributes(attrs, R.styleable.HorizontalHistogramView)

            // x轴刻度文案颜色
            mXAxisScaleTextColor = ta.getColor(R.styleable.HorizontalHistogramView_histogram_chart_x_axis_scale_text_color,mXAxisScaleTextColor)
            // x轴刻度文案文字大小
            mXAxisScaleTextSize = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_x_axis_scale_text_size,mXAxisScaleTextSize)
            // x轴刻度文案和刻度的距离
            mXAxisScaleTextPadding = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_x_axis_scale_text_padding,mXAxisScaleTextPadding)
            // x轴刻度文案倾斜角度
            mXAxisScaleTextAngle = ta.getFloat(R.styleable.HorizontalHistogramView_histogram_chart_x_axis_scale_text_angle,mXAxisScaleTextAngle)


            // y轴刻度文案颜色
            mYAxisScaleTextColor = ta.getColor(R.styleable.HorizontalHistogramView_histogram_chart_y_axis_scale_text_color,mYAxisScaleTextColor)
            // y轴刻度文案文字大小
            mYAxisScaleTextSize = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_y_axis_scale_text_size,mYAxisScaleTextSize)
            // y轴刻度文案和刻度的距离
            mYAxisScaleTextPadding = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_y_axis_scale_text_padding,mYAxisScaleTextPadding)
            // y轴刻度文案最大字数
            mYAxisScaleTextMaxCount = ta.getInt(R.styleable.HorizontalHistogramView_histogram_chart_y_axis_scale_text_max_count,mYAxisScaleTextMaxCount)

            // 总数文字大小
            mTotalTextSize = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_total_text_size,mTotalTextSize)
            // 总数文字颜色
            mTotalTextColor = ta.getColor(R.styleable.HorizontalHistogramView_histogram_chart_total_text_color,mTotalTextColor)
            // 总数到图形的距离
            mTotalTextPadding = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_total_text_padding,mTotalTextPadding)
            // 总数文字是否加粗
            mTotalTextBold = ta.getBoolean(R.styleable.HorizontalHistogramView_histogram_chart_total_text_bold,mTotalTextBold)

            // 每个线条的高度
            mStripHeight = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_strip_height,mStripHeight)
            // 线条的间隔
            mStripPadding = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_strip_padding,mStripPadding)
            // 线条每部分的间隔
            mStripPartDivider = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_strip_part_divider,mStripPartDivider)

            mChartToLeft = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_to_left,mChartToLeft)
            mChartToRight = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_to_right,mChartToRight)
            mChartToTop = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_to_top,mChartToTop)
            mChartToBottom = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_to_bottom,mChartToBottom)

            // <!-- 刻度虚线线宽 -->
            mDashLineWidth = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_dash_line_width,mDashLineWidth)
            // <!-- 刻度虚线空白宽度 -->
            mDashLineSpace = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_dash_line_space,mDashLineSpace)
            // <!-- 刻度虚线高度 -->
            mDashLineHeight = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_chart_dash_line_height,mDashLineHeight)
            // <!-- 刻度虚线颜色 -->
            mDashLineColor = ta.getColor(R.styleable.HorizontalHistogramView_histogram_chart_dash_line_color,mDashLineColor)

            ta.recycle()
        }

        // paint
        initTextPaint(mXAxisScaleTextPaint,mXAxisScaleTextSize,mXAxisScaleTextColor)
        initTextPaint(mYAxisScaleTextPaint,mYAxisScaleTextSize,mYAxisScaleTextColor)

        initLinePaint(mStripPaint,mStripHeight,Color.BLACK)

        initTextPaint(mTotalTextPaint,mTotalTextSize,mTotalTextColor)
        mTotalTextPaint.isFakeBoldText = mTotalTextBold

        initLinePaint(mDashLinePaint,mDashLineWidth,mDashLineColor)

        initInfoView(context,attrs)
    }

    private fun initInfoView(context: Context?, attrs: AttributeSet?){
        var paddingLeft = 36f
        var paddingRight = 36f
        var paddingTop = 36f
        var paddingBottom = 36f
        var contentBgColor = Color.WHITE
        var titleTextSize = 36f
        var titleTextBold = true
        var titleTextColor = Color.BLACK
        var titleToBottom = 24f
        var itemTextSize = 36f
        var itemTextPadding = 12f
        var itemTextBold = true
        var rectRadius = 6
        var shadowSize = 24
        if(context != null) {
            val ta = context.obtainStyledAttributes(attrs, R.styleable.HorizontalHistogramView)

            paddingLeft = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_info_padding_left,paddingLeft)
            paddingTop = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_info_padding_top,paddingTop)
            paddingRight = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_info_padding_right,paddingRight)
            paddingBottom = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_info_padding_bottom,paddingBottom)


            contentBgColor = ta.getColor(R.styleable.HorizontalHistogramView_histogram_info_content_bg_color,contentBgColor)

            titleTextSize = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_info_title_text_size,titleTextSize)
            titleTextBold = ta.getBoolean(R.styleable.HorizontalHistogramView_histogram_info_title_text_bold,titleTextBold)
            titleTextColor = ta.getColor(R.styleable.HorizontalHistogramView_histogram_info_title_text_color,titleTextColor)
            titleToBottom = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_info_title_text_to_bottom,titleToBottom)


            itemTextSize = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_info_item_text_size,itemTextSize)
            itemTextBold = ta.getBoolean(R.styleable.HorizontalHistogramView_histogram_info_item_text_bold,itemTextBold)
            itemTextPadding = ta.getDimension(R.styleable.HorizontalHistogramView_histogram_info_item_text_padding,itemTextPadding)

            rectRadius = ta.getDimensionPixelOffset(R.styleable.HorizontalHistogramView_histogram_info_rect_radius,rectRadius)
            shadowSize = ta.getDimensionPixelOffset(R.styleable.HorizontalHistogramView_histogram_info_shadow_size,shadowSize)
            ta.recycle()
        }

        mDrawInfoHelper = DrawInfoBuilder()
            .paddingLeft(paddingLeft)
            .paddingRight(paddingRight)
            .paddingTop(paddingTop)
            .paddingBottom(paddingBottom)
            .contentBgColor(contentBgColor)
            .titleTextSize(titleTextSize)
            .titleTextBold(titleTextBold)
            .titleTextColor(titleTextColor)
            .titleToBottom(titleToBottom)
            .itemTextSize(itemTextSize)
            .itemTextPadding(itemTextPadding)
            .itemTextBold(itemTextBold)
            .rectRadius(rectRadius)
            .shadowSize(shadowSize)
            .build()
    }

    private fun initLinePaint(paint: Paint,size: Float,@ColorInt color: Int){
        paint.style = Paint.Style.STROKE
        paint.strokeWidth = size
        paint.color = color
    }

    private fun initTextPaint(paint: Paint,textSize: Float,@ColorInt color: Int){
        paint.textSize = textSize
        paint.color = color
    }

    fun setData(data: HistogramData?){
        mData = data

        receiveData(data)

        requestLayout()
        postInvalidate()
    }

    private var mTotalMaxValue = 0.0
    private var mTotalMaxTextLength = 0.0f
    private var mYAxisScaleTextMaxLength = 0.0f
    private var mXAxisScaleTextMaxLength = 0.0f
    private var mXAxisScaleMax = 0.0f

    private fun receiveData(data: HistogramData?) {
        if(data == null || data.list.isEmpty() || data.xAxisCount <= 0){
            return
        }
        val list = data.list

        var tempTotalValue = 0.0        // 计算出来的
        list.forEach {
            val totalText = it.total.toString()
            if(totalText.indexOf(".") < 0){     // 是int值
                val total = it.total.toInt()
                if(tempTotalValue < total){
                    tempTotalValue = total.toDouble()
                }
                it.totalText = ChartTextUtils.commaSeparationNumber(total)
                val totalTextLength = mTotalTextPaint.measureText(it.totalText)
                if(totalTextLength > mTotalMaxTextLength){
                    mTotalMaxTextLength = totalTextLength
                }
            }else{      // 是double值
                val total = it.total.toDouble()
                if(tempTotalValue < total){
                    tempTotalValue = total
                }
                it.totalText = ChartTextUtils.commaSeparationNumber(total)
                val totalTextLength = mTotalTextPaint.measureText(it.totalText)
                if(totalTextLength > mTotalMaxTextLength){
                    mTotalMaxTextLength = totalTextLength
                }
            }



            // 修改Y轴文字
            it.ellipseName = ChartTextUtils.ellipseTextEnd(it.name,mYAxisScaleTextMaxCount)
            val ellipseNameLength = mYAxisScaleTextPaint.measureText(it.ellipseName)
            if(ellipseNameLength > mYAxisScaleTextMaxLength){
                mYAxisScaleTextMaxLength = ellipseNameLength
            }
        }
        mTotalMaxValue = tempTotalValue

        val maxValue = mTotalMaxValue.roundToInt()
        val count = data.xAxisCount
        val mode = maxValue % count
        val maxScaleValue = if (mode == 0) {
            maxValue
        } else {
            maxValue + count - mode
        }
        val unit = maxScaleValue / count
        for(index in 0..count){
            val text = ChartTextUtils.commaSeparationNumber(index * unit)
            mXAxisScaleTextList.add(text)
            if(index == count){
                mXAxisScaleTextMaxLength = mXAxisScaleTextPaint.measureText(text)
                mXAxisScaleMax = (index * unit).toFloat()
            }
        }
    }

    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
        val list = mData?.list
        if(list != null && list.size > 0){
            val size = list.size
            val padding = paddingTop + paddingBottom
            val topBottom = mChartToTop + mChartToBottom
            val heightMode = MeasureSpec.EXACTLY
            val degress = Math.toRadians(mXAxisScaleTextAngle.toDouble())
            val sinValue = sin(degress)
            val maxTextHeight =  if(sinValue == 0.0) {
                mXAxisScaleTextSize.toDouble()
            }else{
                mXAxisScaleTextMaxLength * sinValue
            }
            // 矩形旋转相差的高度
            val sizeDelta = mXAxisScaleTextSize * cos(degress)
            val heightSize = padding + topBottom + size * (mStripHeight + mStripPadding) + mXAxisScaleTextPadding + maxTextHeight + sizeDelta
            val heightSpec = MeasureSpec.makeMeasureSpec(ceil(heightSize).roundToInt(),heightMode)

            super.onMeasure(widthMeasureSpec, heightSpec)
        }else{
            super.onMeasure(widthMeasureSpec, heightMeasureSpec)
        }
        receiveLocationInfo()
    }

    private val mXAxisStartPointF = PointF()
    private val mXAxisEndPointF = PointF()
    private val mYAxisStartPointF = PointF()
    private val mYAxisEndPointF = PointF()
    private var mYAxisScaleTextRect = RectF()

    private fun receiveLocationInfo() {
        // y轴刻度最大矩形
        mYAxisScaleTextRect.left = paddingLeft + mChartToLeft
        mYAxisScaleTextRect.right = mYAxisScaleTextRect.left + mYAxisScaleTextMaxLength

        // X轴开始点和X轴结束点坐标（其y轴坐标时一样的）
        mXAxisStartPointF.x = mYAxisScaleTextRect.right + mYAxisScaleTextPadding
        mXAxisStartPointF.y = paddingTop + mChartToTop + (mData?.list?.size ?: 0) * (mStripHeight + mStripPadding)
        mXAxisEndPointF.x = measuredWidth - paddingRight - mChartToRight - mTotalMaxTextLength - mTotalTextPadding
        mXAxisEndPointF.y = mXAxisStartPointF.y

        receiveClickLocationInfo()
    }

    private val mClickRectList = ArrayList<RectF>()
    private fun receiveClickLocationInfo() {
        val data = mData ?: return
        val list = data.list
        val size = list.size
        if(size == 0){
            return
        }
        mClickRectList.clear()
        val length = mXAxisEndPointF.x - mXAxisStartPointF.x

        var drawY = mXAxisStartPointF.y - mStripPadding - mStripHeight / 2
        val startX = paddingLeft + mChartToLeft
        val endX = measuredWidth - paddingRight - mChartToRight

        val halfClickHeight =  mStripPadding / 2 + mStripHeight / 2

        for(index in 0 until size){
            val rectF = RectF()
            rectF.left = startX
            rectF.right = endX
            rectF.top = drawY - halfClickHeight
            rectF.bottom = drawY + halfClickHeight
            drawY = drawY - mStripPadding - mStripHeight
            mClickRectList.add(rectF)
        }
    }

    private val mDrawRect = Rect()

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        val data = mData
        if(canvas == null || data == null){
            return
        }
        val list = data.list
        val xAxisCount = data.xAxisCount
        if(list.isEmpty() || xAxisCount <= 0){
            return
        }
        drawDashLine(canvas,xAxisCount)
        drawXAxis(canvas,xAxisCount)
        drawYAxis(canvas,list)

        if(mShowing) {
            val item = mClickItem
            if (mClickX >= 0 && mClickY >= 0 && mShowing && item != null) {
                mDrawRect.left = paddingLeft
                mDrawRect.top = paddingTop
                mDrawRect.right = width - paddingRight - paddingLeft
                mDrawRect.bottom = height - paddingBottom - paddingTop
                mDrawInfoHelper?.drawInfo(canvas, item, mClickX, mClickY, mDrawRect)
            }
        }
    }

    // 绘制虚线
    private fun drawDashLine(canvas: Canvas,xAxisCount: Int) {
        val startX = mXAxisStartPointF.x
        val endX = mXAxisEndPointF.x
        var startY = mXAxisStartPointF.y
        val endY = startY - (mData?.list?.size ?: 0) * (mStripHeight + mStripPadding)
        val dashLineDistance = (endX - startX) / xAxisCount
        while(startY - mDashLineHeight > endY){
            for(index in 0..xAxisCount){
                val x = startX + index * dashLineDistance
                canvas.drawLine(x,startY,x,startY - mDashLineHeight,mDashLinePaint)
            }
            startY -= (mDashLineHeight + mDashLineSpace)
        }
    }

    private fun drawXAxis(canvas: Canvas,xAxisCount: Int) {
        val list = mXAxisScaleTextList
        val size = list.size
        if(size == 0){
            return
        }

        val perWidth = (mXAxisEndPointF.x - mXAxisStartPointF.x) / xAxisCount
        var startScaleX = mXAxisStartPointF.x
        val endScaleY = mXAxisStartPointF.y
        val startScaleTextY = endScaleY + mXAxisScaleTextPadding
        val endScaleTextY = startScaleTextY + mXAxisScaleTextSize

        val halfTextSize = mXAxisScaleTextSize / 2f

        for(index in 0 until size){
            val item = list[index]
            canvas.save()
            canvas.rotate(mXAxisScaleTextAngle,startScaleX + halfTextSize,startScaleTextY)
            DrawTextUtils.drawTextOnRectCenterLeft(canvas,
                mXAxisScaleTextPaint,
                item,
                startScaleX + halfTextSize,
                startScaleTextY,
                startScaleX + halfTextSize + mXAxisScaleTextMaxLength,
                endScaleTextY)
            canvas.restore()
            startScaleX += perWidth
        }
    }

    private fun drawYAxis(canvas: Canvas, list: ArrayList<HistogramItem>) {

        val length = mXAxisEndPointF.x - mXAxisStartPointF.x

        var startY = mXAxisStartPointF.y - mStripPadding
        val startX = paddingLeft + mChartToLeft
        val endX = startX + mYAxisScaleTextMaxLength


        val max = mXAxisScaleMax
        list.forEach {
            DrawTextUtils.drawTextOnRectCenterRight(
                canvas,mYAxisScaleTextPaint, it.ellipseName,
                startX,
                startY,
                endX,
                startY - mStripHeight)
            var stripStartX = mXAxisStartPointF.x
            // 绘制当前柱状图
            if(max > 0) {
                val partList = it.partList
                val partListSize = partList.size
                for(index in 0 until partListSize){
                    val item = partList[index]
                    val y = startY - mStripHeight / 2f
                    val delta = if(index == partListSize - 1 || index == 0){
                        mStripPartDivider / 2
                    }else{
                        mStripPartDivider
                    }
                    val stripEndX = (stripStartX +  item.value.toDouble() / max * length - delta).toFloat()
                    mStripPaint.color = item.color
                    canvas.drawLine(stripStartX, y, stripEndX, y, mStripPaint)

                    // 绘制总数文字
                    if(index == partListSize - 1){
                        val totalTextLeft = stripEndX + mTotalTextPadding
                        val totalTextTop = y - mTotalTextSize / 2f
                        val text = it.totalText
                        DrawTextUtils.drawTextOnRectCenter(canvas,mTotalTextPaint,text,
                            totalTextLeft,
                            totalTextTop,
                            totalTextLeft + mTotalTextPaint.measureText(text),
                            totalTextTop + mTotalTextSize)
                    }else{
                        stripStartX = stripEndX + mStripPartDivider
                    }
                }
            }

            startY -= (mStripPadding + mStripHeight)
        }
    }

    private var mDownX = -1f
    private var mDownY = -1f

    override fun dispatchTouchEvent(event: MotionEvent?): Boolean {
        val action = event?.action
        if(action != null){
            when(action){
                MotionEvent.ACTION_DOWN -> {
                    mDownX = event.x
                    mDownY = event.y
                }
                MotionEvent.ACTION_UP -> {
                    onTouchUp(mDownX,mDownY,event.x,event.y,event)
                    mDownX = -1f
                    mDownY = -1f
                }
                MotionEvent.ACTION_CANCEL -> {
                    mDownX = -1f
                    mDownY = -1f
                }
            }
        }
        return true
    }

    private fun onTouchUp(
        downX: Float, downY: Float,
        upX: Float, upY: Float, event: MotionEvent) {

        val list = mClickRectList
        val size = list.size

        if(size == 0){
            return
        }

        for(index in 0 until size){
            val rect = list[index]
            if(rect.contains(downX,downY) && rect.contains(upX,upY)){
                val dataList = mData?.list ?: return
                if(index in dataList.indices){
                    val item = dataList[index]
                    mClickItem = item
                    mClickX = upX
                    mClickY = upY

                    if(mShowing){
                        val inRect = mDrawInfoHelper?.isPointInInfoView(upX,upY) ?: false
                        if(!inRect) {
                            dismissInfoView()
                        }
                    }else{
                        showInfoView()
                    }

                    mOnItemClickListener?.onItemClick(index,item,upX,upY,event.rawX,event.rawY)
                }
                return
            }
        }
    }

    private fun showInfoView(){
        mShowing = true
        invalidate()
    }

    fun dismissInfoView(){
        if(mShowing){
            mShowing = false
            invalidate()
        }
    }

    private var mClickItem: HistogramItem? = null
    private var mClickX = -1f
    private var mClickY = -1f
    private var mShowing = false

    private var mOnItemClickListener: OnItemClickListener? = null

    fun setOnItemClickListener(listener: OnItemClickListener?){
        mOnItemClickListener = listener
    }

    interface OnItemClickListener {
        fun onItemClick(index: Int,item: HistogramItem,xInView: Float,yInView: Float,rawX: Float,rawY: Float)
    }
}